#ifndef G_GNUC_NULL_TERMINATED
# if __GNUC__ >= 4
#  define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
# else
#  define G_GNUC_NULL_TERMINATED
# endif
#endif

// GNU C libraries
#include <stdio.h>
#include <string.h>

// Glib
#include <glib.h>

// Libpurple functions
#include "config.h"
#include "version.h"
#include "util.h"
#include "request.h"
#include "accountopt.h"
#include "debug.h"
#include "cipher.h"

// Jabber functions
#include "jabber.h"
#include "message.h"
#include "presence.h"

#if PURPLE_MAJOR_VERSION < 3
#define purple_notify_user_info_add_pair_html(user_info, label, value) \
	purple_notify_user_info_add_pair((user_info), (label), (value))
#define purple_util_fetch_url_request(account, url, full, user_agent, http11, request, include_headers, max_len, callback, data) \
	purple_util_fetch_url_request((url), (full), (user_agent), (http11), (request), (include_headers), (callback), (data))
#endif

#ifndef _
#define _(a) (a)
#define N_(a) (a)
#endif

#define FACEBOOK_APP_ID "107899262577150"
#define FACEBOOK_API_KEY "ff26b9e09239e33babaeaf747c778926"
#define FACEBOOK_SECRET "57a7da14aa16458ab1490fd305b6f545"
#define FACEBOOK_PLUGIN_ID "prpl-bigbrownchunx-facebooknew"

typedef struct {
	JabberStream js;
	JabberStream padding;
	
	PurpleConnection *pc;
	PurpleAccount *account;
	gchar *last_status_message;
	gint last_notification_time;
	gint last_profile_update_time;
	
	guint status_message_timeout;
	guint unread_count_timeout;
	guint notifications_timeout;
	
	GHashTable *sent_messages_hash;
} FacebookJabberStream ;

static const gchar *jid_to_uid(const gchar *jid)
{
	static gchar *uid = NULL;
	gchar *jid_copy;
	g_free(uid);
	
	if (!jid) return NULL;
	
	jid_copy = g_strdup(jid);
	purple_util_chrreplace(jid_copy, '@', '\0');
	uid = g_strdup(jid_copy[0] == '-' ? jid_copy + 1 : jid_copy);
	g_free(jid_copy);
	
	return uid;
}

static const gchar *uid_to_jid(const gchar *uid)
{
	static gchar *jid = NULL;
	g_free(jid);
	
	jid = g_strconcat("-", uid, "@chat.facebook.com", NULL);
	
	return jid;
}

static void BLANK_URL_CALLBACK(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message){}

#define fb_util_get_url(url, callback, data) purple_util_fetch_url_request(NULL, url, TRUE, NULL, FALSE, NULL, FALSE, -1, callback, data)

static PurpleUtilFetchUrlData *
fb_util_post_url(const gchar *url, const gchar *postdata, PurpleUtilFetchUrlCallback callback, gpointer data)
{
	PurpleUtilFetchUrlData *urldata;
	gchar *host, *path, *request;
	int port;
	
	purple_url_parse(url, &host, &port, &path, NULL, NULL);
	request = g_strdup_printf("POST %s HTTP/1.0\r\n"
			"Connection: close\r\n"
			"Accept: */*\r\n"
			"Host: %s:%d\r\n"
			"Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n"
			"Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n%s",
			path, host, port, strlen(postdata), postdata);
	
	urldata = purple_util_fetch_url_request(NULL, url, TRUE, NULL, FALSE, request, FALSE, -1, callback, data);
	
	g_free(host);
	g_free(path);
	g_free(request);
	
	return urldata;
}




static void fb_set_status_ok_cb(gpointer data, const gchar *status_text)
{
	PurpleConnection *pc = data;
	PurpleAccount *account = pc->account;
	FacebookJabberStream *fjs = pc->proto_data;
	gchar *postdata;
	gchar *status_tmp;
	gchar *status_text_new;
	const gchar *access_token;
	
	access_token = purple_account_get_string(account, "access_token", NULL);
	g_return_if_fail(access_token != NULL);
	
	status_text_new = g_strstrip(g_strdup(status_text));
	
	/* don't set the status if it's idential to what we've already set */
	if (fjs->last_status_message && g_str_equal(fjs->last_status_message, status_text_new)) {
		g_free(status_text_new);
		return;
	}
	
	g_free(fjs->last_status_message);
	fjs->last_status_message = status_text_new;
	
	status_tmp = g_strdup(purple_url_encode(status_text_new));
	postdata = g_strdup_printf("access_token=%s&message=%s", access_token, status_tmp);
	g_free(status_tmp);
	
	fb_util_post_url("https://graph.facebook.com/me/feed", postdata, BLANK_URL_CALLBACK, NULL);
	
	g_free(postdata);
}

static void fb_set_status_cb(PurplePluginAction *action)
{
	PurpleConnection *pc = action->context;
	PurpleAccount *account = pc->account;
	
	purple_request_input(pc, NULL, _("Set your Facebook status"),
					purple_account_get_alias(account), "is ",
					FALSE, FALSE, NULL, _("OK"),
					G_CALLBACK(fb_set_status_ok_cb), _("Cancel"),
					NULL, account, NULL, NULL, pc);
}

static void fb_set_status_p(PurpleAccount *account, PurpleStatus *status)
{
	const gchar *message;
	gchar *stripped;

	jabber_set_status(account, status);

	/* first check that we actually want to set this through Pidgin */
	if (!purple_account_get_bool(account, "facebook_set_status_through_pidgin", FALSE))
	{
		return;
	}

	message = purple_status_get_attr_string(status, "message");
	if (message == NULL) message = "";

	stripped = purple_markup_strip_html(message);
	fb_set_status_ok_cb(account->gc, stripped);
	g_free(stripped);
}

typedef void (*FQLQueryCallBack)(PurpleConnection *pc, const xmlnode *response, gpointer user_data);
typedef struct {
	PurpleConnection *pc;
	FQLQueryCallBack success_callback;
	FQLQueryCallBack error_callback;
	gpointer user_data;
} FQL;

static void fql_query_result(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
{
	FQL *fql = user_data;
	xmlnode *fql_response;
	
	g_return_if_fail(fql != NULL);
	
	fql_response = xmlnode_from_str(url_text, len);
	
	if ((error_message && *error_message) ||
		!fql_response || !len ||
		g_str_equal(fql_response->name, "error_response"))
	{
		if (fql->error_callback)
			fql->error_callback(fql->pc, fql_response, fql->user_data);
	} else if (fql_response && g_str_equal(fql_response->name, "fql_query_response"))
	{
		if (fql->success_callback)
			fql->success_callback(fql->pc, fql_response, fql->user_data);
	}
	
	g_free(fql);
	xmlnode_free(fql_response);
}

static void fql_query(PurpleConnection *pc, const gchar *query, FQLQueryCallBack success_callback, FQLQueryCallBack error_callback, gpointer user_data)
{
	FQL *fql;	
	PurpleAccount *account = pc->account;
	const gchar *access_token = purple_account_get_string(account, "access_token", NULL);
	gchar *query_encoded, *url;
	
	if (access_token == NULL) return;
	
	purple_debug_info("facebook", "FQL: %s\n", query);
	
	fql = g_new0(FQL, 1);
	fql->pc = pc;
	fql->success_callback = success_callback;
	fql->error_callback = error_callback;
	fql->user_data = user_data;
	
	query_encoded = g_strdup(purple_url_encode(query));
	url = g_strconcat("https://api.facebook.com/method/fql.query?query=", query_encoded, "&access_token=", access_token, "&format=xml", NULL);
	
	fb_util_get_url(url, fql_query_result, fql);
	
	g_free(url);
	g_free(query_encoded);
}

static void fb_got_account_alias(PurpleConnection *pc, const xmlnode *response, gpointer user_data)
{
	xmlnode *user, *name;
	gchar *name_str;
	
	user = xmlnode_get_child(response, "user");
	name = xmlnode_get_child(user, "name");
	name_str = xmlnode_get_data(name);
	
	purple_account_set_alias(pc->account, name_str);
	
	g_free(name_str);
}

static void fb_got_status_messages(PurpleConnection *pc, const xmlnode *response, gpointer user_data)
{
	FacebookJabberStream *fjs = pc->proto_data;
	xmlnode *user;
	gint last_profile_update_time = 0;
	
	gchar *uid, *jid, *message_str, *online_presence, *name, *profile_update_time;
	xmlnode *status, *message;
	
	PurpleGroup *group = NULL;
	PurpleBuddy *buddy;
	
	//jabber_process_packet
	for(user = xmlnode_get_child(response, "user"); user; user = xmlnode_get_next_twin(user))
	{
		uid = xmlnode_get_data(xmlnode_get_child(user, "uid"));
		name = xmlnode_get_data(xmlnode_get_child(user, "name"));
		profile_update_time = xmlnode_get_data(xmlnode_get_child(user, "profile_update_time"));
		status = xmlnode_get_child(user, "status");
		message = xmlnode_get_child(status, "message");
		if (message)
		{
			online_presence = xmlnode_get_data(xmlnode_get_child(user, "online_presence"));
			message_str = xmlnode_get_data(message);
			jid = g_strdup(uid_to_jid(uid));
			
			buddy = purple_find_buddy(pc->account, jid);
			if (!buddy)
			{
				if (!group)
				{
					group = purple_find_group("Facebook");
					if (!group)
					{
						group = purple_group_new("Facebook");
						purple_blist_add_group(group, NULL);
					}
				}
				buddy = purple_buddy_new(pc->account, jid, NULL);
				purple_blist_add_buddy(buddy, NULL, group, NULL);
			}
			
			if (online_presence && message_str && jid)
			{
				//Don't update the message if the message is already set
				gchar *old_message = jabber_status_text(purple_find_buddy(pc->account, jid));
				if (old_message == NULL || g_str_equal(old_message, message_str) == FALSE)
				{
					const gchar *jabber_status;
					
					if (g_str_equal(online_presence, "offline"))
						jabber_status = jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE);
					else if (g_str_equal(online_presence, "idle"))
						jabber_status = jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_AWAY);
					else
						jabber_status = jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_ONLINE);
					
					purple_prpl_got_user_status(pc->account, jid, jabber_status, "message", message_str, NULL);
				}
				g_free(old_message);
			}
			g_free(message_str);
			g_free(jid);
			g_free(online_presence);
		}
		
		if (profile_update_time != NULL)
			last_profile_update_time = MAX(last_profile_update_time, atoi(profile_update_time));
		
		g_free(profile_update_time);
		g_free(uid);
		g_free(name);
	}
	
	if (last_profile_update_time > 0)
	{
		purple_debug_info("facebook", "Updating last_profile_update_time from %d to %d\n", fjs->last_profile_update_time, last_profile_update_time);
		fjs->last_profile_update_time = last_profile_update_time;
	}
}

static gboolean fb_fetch_status_messages(PurpleConnection *pc)
{
	FacebookJabberStream *fjs = pc->proto_data;
	
	if (pc->state == PURPLE_CONNECTED)
	{
		const gchar *base_status_query = "SELECT uid,status,name,username,pic_big,online_presence,profile_update_time FROM user WHERE (uid IN (SELECT uid2 FROM friend WHERE uid1=me())";
		gchar *status_query, *query;
		
		if (purple_account_get_bool(pc->account, "facebook_hide_self", TRUE))
		{
			status_query = g_strconcat(base_status_query, ")", NULL);
		} else {
			status_query = g_strconcat(base_status_query, " OR uid=me())", NULL);
		}
		
		if (fjs->last_profile_update_time > 0)
		{
			query = g_strdup_printf("%s AND profile_update_time > %d", status_query, fjs->last_profile_update_time);
		} else {
			query = g_strdup(status_query);
		}
		
		fql_query(pc, query, fb_got_status_messages, NULL, NULL);
		g_free(query);
		g_free(status_query);
	}
	
	return TRUE;
}

static void fb_got_notifications(PurpleConnection *pc, const xmlnode *response, gpointer user_data)
{
	FacebookJabberStream *fjs = pc->proto_data;
	gint updated_timestamp;
	
	xmlnode *notification;
	for(notification = xmlnode_get_child(response, "notification"); notification; notification = xmlnode_get_next_twin(notification))
	{
		gchar *sender_id = xmlnode_get_data(xmlnode_get_child(notification, "sender_id"));
		gchar *title_text = xmlnode_get_data(xmlnode_get_child(notification, "title_text"));
		gchar *href = xmlnode_get_data(xmlnode_get_child(notification, "href"));
		gchar *updated_time;
		
		purple_notify_email(pc, title_text, uid_to_jid(sender_id), pc->account->username, href, NULL, NULL);
		
		g_free(href);
		g_free(title_text);
		g_free(sender_id);
		
		updated_time = xmlnode_get_data(xmlnode_get_child(notification, "updated_time"));
		updated_timestamp = atoi(updated_time);
		if (updated_timestamp > fjs->last_notification_time)
			fjs->last_notification_time = updated_timestamp;
		g_free(updated_time);
	}
}

static gboolean fb_fetch_notifications(PurpleConnection *pc)
{
	FacebookJabberStream *fjs = pc->proto_data;
	
	if (pc->state == PURPLE_CONNECTED)
	{
		gchar *query = g_strdup_printf("SELECT notification_id,sender_id,title_text,href,updated_time FROM notification WHERE recipient_id=me() AND is_unread=1 AND is_hidden=0 AND updated_time > %d", fjs->last_notification_time);
		fql_query(pc, query, fb_got_notifications, NULL, NULL);
	}
	
	return TRUE;
}

static void fb_got_unread_count(PurpleConnection *pc, const xmlnode *response, gpointer user_data)
{
	gchar *unread_count;
	gchar *url;
	
	unread_count = xmlnode_get_data(xmlnode_get_child(xmlnode_get_child(response, "mailbox_folder"), "unread_count"));
	url = g_strdup("http://www.facebook.com/inbox/");
	purple_notify_emails(pc, atoi(unread_count), FALSE, NULL, NULL, (const char**) &(pc->account->username), (const char**) &(url), NULL, NULL);
	g_free(url);
}

static gboolean fb_fetch_unread_count(PurpleConnection *pc)
{
	//if (pc->state == PURPLE_CONNECTED)
	//{
	//	fql_query(pc, "SELECT unread_count FROM mailbox_folder WHERE folder_id=0 AND viewer_id=me()", fb_got_unread_count, NULL, NULL);
	//}
	//return TRUE;
	
	return FALSE;
}

static void fb_set_access_token_cb(gpointer data, const gchar *access_token)
{
	PurpleConnection *pc = data;
	PurpleAccount *account = pc->account;
	FacebookJabberStream *fjs = pc->proto_data;
	gchar *real_access_token;
	const gchar *access_token_start;
	gchar *url;
	
	if((access_token_start = strstr(access_token, "access_token=")))
	{
		access_token_start += 13;
		real_access_token = g_strndup(access_token_start, strchr(access_token_start, '&') - access_token_start);
	} else {
		real_access_token = g_strdup(access_token);
	}
	
	purple_account_set_string(account, "access_token", purple_url_decode(real_access_token));
	
	if (fjs->status_message_timeout == 0)
	{
		fb_fetch_status_messages(pc);
		purple_timeout_add_seconds(60, (GSourceFunc) fb_fetch_status_messages, pc);
	}
	if (fjs->unread_count_timeout == 0)
	{
		fb_fetch_unread_count(pc);
		purple_timeout_add_seconds(60, (GSourceFunc) fb_fetch_unread_count, pc);
	}
	if (fjs->notifications_timeout == 0)
	{
		fb_fetch_notifications(pc);
		purple_timeout_add_seconds(60, (GSourceFunc) fb_fetch_notifications, pc);
	}
	
	url = g_strdup_printf("https://api.facebook.com/method/auth.promoteSession?access_token=%s", real_access_token);
	fb_util_get_url(url, NULL, NULL);
	g_free(url);
	
	g_free(real_access_token);
}

static void fb_request_access_token(PurpleConnection *pc)
{
	PurpleAccount *account = pc->account;
	
	purple_request_input(pc, NULL, _("Set your Access Token"),
					_("Copy the Pidgin URL you are sent to after you accept"), NULL,
					FALSE, FALSE, "http://pidgin.im/#access_token=...", _("OK"),
					G_CALLBACK(fb_set_access_token_cb), _("Cancel"),
					NULL, account, NULL, NULL, pc);
}

static void fb_set_access_token(PurplePluginAction *action)
{
	PurpleConnection *pc = action->context;
	fb_request_access_token(pc);
}

static void fb_request_auth()
{
	PurplePlugin *handle = purple_find_prpl(FACEBOOK_PLUGIN_ID);
	
	purple_notify_uri(handle, "https://www.facebook.com/dialog/oauth?client_id=" FACEBOOK_APP_ID "&redirect_uri=http://pidgin.im/&response_type=token&scope=offline_access,xmpp_login,read_stream,publish_stream,read_mailbox,manage_friendlists,manage_notifications");
}

/**  
AUTH MECH STUFF
*/
static JabberSaslState 
fb_sasl_handle_challenge(JabberStream *js, xmlnode *packet, xmlnode **response, char **msg) 
{
	xmlnode *reply = NULL;
	gchar *challenge;
	guchar *decoded;
	gsize decoded_len;
	gchar **pairs, *method, *nonce;
	gsize i;
	gchar *request;
	gchar *enc_out;
	const gchar *access_token;

	/* Get base64-encoded challenge from XML */ 
	challenge = xmlnode_get_data(packet);
	if (challenge == NULL) { 
		*msg = g_strdup(_("Invalid response from server"));
		return JABBER_SASL_STATE_FAIL;
	} 

	/* Decode challenge */ 
	decoded = purple_base64_decode(challenge, &decoded_len);
	if (decoded == NULL) { 
		purple_debug_error("jabber", "X-FACEBOOK-PLATFORM challenge wasn't valid base64: %s\n", challenge);

		*msg = g_strdup(_("Invalid response from server"));

		g_free(challenge);
		return JABBER_SASL_STATE_FAIL;
	} 
	g_free(challenge);

	/* NULL-terminate the challenge so we can parse it */ 
	challenge = g_strdup(purple_url_decode((const gchar *)decoded));
	g_free(decoded);
	purple_debug_misc("jabber", "X-FACEBOOK-PLATFORM decoded challenge is %s\n", challenge);

	/* Get method and nonce */ 
	method = NULL;
	nonce = NULL;
	pairs = g_strsplit(challenge, "&", 0);
	for (i = 0; pairs[i] != NULL; i++) { 
		if (g_str_has_prefix(pairs[i], "method=")) { 
			g_free(method);
			method = g_strdup((strchr(pairs[i], '=') + 1));
		} else if (g_str_has_prefix(pairs[i], "nonce=")) { 
			g_free(nonce);
			nonce = g_strdup((strchr(pairs[i], '=') + 1));
		} 
	} 
	g_strfreev(pairs);
	if (!method || !nonce) { 
		purple_debug_error("jabber", "X-FACEBOOK-PLATFORM challenge is missing method or nonce: %s\n", challenge);
		*msg = g_strdup(_("Invalid response from server"));

		g_free(method);
		g_free(nonce);
		g_free(challenge);
		return JABBER_SASL_STATE_FAIL;
	} 
	g_free(challenge);
	
	access_token = purple_account_get_string(purple_connection_get_account(js->gc), "access_token", "||");
	request = g_strdup_printf("api_key=%s&call_id=%d&method=%s&nonce=%s&access_token=%s&v=1.0", FACEBOOK_API_KEY, (int)time(NULL), method, nonce, purple_url_encode(access_token));
	
	g_free(method);
	g_free(nonce);

	purple_debug_misc("jabber", "X-FACEBOOK-PLATFORM response before encoding is %s\n", request);
	enc_out = purple_base64_encode((const guchar *)request, strlen(request));
	g_free(request);

	reply = xmlnode_new("response");
	xmlnode_set_namespace(reply, NS_XMPP_SASL);
	xmlnode_insert_data(reply, enc_out, -1);

	g_free(enc_out);

	*response = reply;

	return JABBER_SASL_STATE_CONTINUE;
} 

static JabberSaslState 
fb_sasl_start(JabberStream *js, xmlnode *packet, xmlnode **response, char **error) 
{
	xmlnode *auth;
	
	if (!purple_account_get_string(purple_connection_get_account(js->gc), "access_token", NULL))
		return JABBER_SASL_STATE_FAIL;

	auth = xmlnode_new("auth");
	xmlnode_set_namespace(auth, "urn:ietf:params:xml:ns:xmpp-sasl");
	xmlnode_set_attrib(auth, "mechanism", "X-FACEBOOK-PLATFORM");

	*response = auth;
	return JABBER_SASL_STATE_CONTINUE;
}

static JabberSaslState
fb_sasl_handle_failure(JabberStream *js, xmlnode *packet, xmlnode **reply, char **error)
{
	JabberSaslMech *digest_mech;
	
	digest_mech = jabber_auth_get_digest_md5_mech();
	if (digest_mech)
	{
		js->auth_mech = digest_mech;
		return digest_mech->start(js, packet, reply, error);
	}
	
	/* Nothing to send */
	return JABBER_SASL_STATE_FAIL;
}

static JabberSaslMech fb_sasl_mech = { 
	120, /* priority */ 
	"X-FACEBOOK-PLATFORM", /* name */ 
	fb_sasl_start, 
	fb_sasl_handle_challenge, /* handle_challenge */ 
	NULL, /* handle_success */ 
	fb_sasl_handle_failure, /* handle_failure */ 
	NULL  /* dispose */ 
};


/**
OTHER STUFF
*/

struct BuddyHashPair {
	PurpleBuddy *buddy;
	gchar *hash;
};

static void fb_got_big_picture(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message)
{
	struct BuddyHashPair *bhp = user_data;
	PurpleBuddy *buddy = bhp->buddy;
	gchar *hash = bhp->hash;
	
	purple_buddy_icons_set_for_user(buddy->account, buddy->name, g_memdup(url_text, len), len, hash);
	
	g_free(hash);
	g_free(bhp);
}

/* Converts *text* into <b>text</b>  and _text_ into <i>text</i> */
static gchar *fb_replace_styled_text(const gchar *text)
{
#ifdef __ARM_EABI__
	return g_strdup(text);
#else /*__ARM_EABI__*/
#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 14
	if (glib_check_version(2, 14, 0))
	{
		return g_strdup(text);
	} else {
		static GRegex *underline_regex = NULL;
		static GRegex *bold_regex = NULL;
		gchar *dup_text;
		gchar *midway_string;
		gchar *output_string;
		
		if (underline_regex == NULL)
		{
			underline_regex = g_regex_new(
				"\\b_([^_\\*]+)_\\b", G_REGEX_OPTIMIZE,
				0, NULL);
		}
		if (bold_regex == NULL)
		{
			bold_regex = g_regex_new(
				"(\\s|^)\\*([^_\\*]+)\\*(?=$|\\s)",
				G_REGEX_OPTIMIZE, 0, NULL);
		}
		
		dup_text = g_strdup(text);
		midway_string = g_regex_replace(underline_regex, dup_text,
			strlen(dup_text), 0, "<u>\\1</u>", 0, NULL);
		if (midway_string == NULL)
		{
			purple_debug_warning("facebook", "regex failed for underline\n");
			return dup_text;
		}
		g_free(dup_text);
		output_string = g_regex_replace(bold_regex, midway_string,
			strlen(midway_string), 0, "\\1<b>\\2</b>", 0, NULL);
		if (output_string == NULL)
		{
			purple_debug_warning("facebook", "regex failed for bold\n");
			return midway_string;
		}
		g_free(midway_string);
		
		return output_string;
	}
#else /* GLIB check */
	return g_strdup(text);
#endif /* GLIB check */
#endif /*__ARM_EABI__*/
}

static void handle_jabber_receiving_xmlnode_signal(PurpleConnection *pc, xmlnode **packet) 
{
	//FacebookJabberStream *fjs = pc->proto_data;
	//xmlnode *node;
	
	if (!g_str_equal(purple_plugin_get_id(pc->prpl), FACEBOOK_PLUGIN_ID))
		return;
	
	//if(xmlnode_get_child(*packet, "mechanisms"))
	//{
		//hack the JabberStream to only use our mech
		//node = xmlnode_get_child(*packet, "mechanisms");
		//xmlnode_insert_data(node, "X-FACEBOOK-PLATFORM", -1);
		//js->auth_mech = facebook_mech;
	//}
	
	// this should probably move to the jabber-receiving-presence signal instead
	if (g_str_equal((*packet)->name, "presence"))
	{
		//Hack the JID to just be the Facebook ID number
		//xmlnode_set_attrib(*packet, "from", jid_to_uid(xmlnode_get_attrib(*packet, "from")));
		
		//Keep their existing status message, if they have one
		PurpleBuddy *buddy;
		PurplePresence *presence;
		PurpleStatus *status;
		const char *message;
		gchar *ret;
		
		buddy = purple_find_buddy(pc->account, xmlnode_get_attrib(*packet, "from"));
		presence = purple_buddy_get_presence(buddy);
		status = purple_presence_get_active_status(presence);
		if((message = purple_status_get_attr_string(status, "message"))) {
			ret = g_markup_escape_text(message, -1);
			if (ret && *ret)
			{
				xmlnode *status = xmlnode_new("status");
				xmlnode_insert_data(status, ret, -1);
				xmlnode_insert_child(*packet, status);
				g_free(ret);
			}
		}
		
		if (xmlnode_get_child(*packet, "x"))
		{
			xmlnode *x = xmlnode_get_child(*packet, "x");
			if (xmlnode_get_child(x, "photo"))
			{
				xmlnode *photo = xmlnode_get_child(x, "photo");
				gchar *photo_hash = xmlnode_get_data(photo);
				const gchar *avatar_hash = purple_buddy_icons_get_checksum_for_user(buddy);
				const gchar *access_token = purple_account_get_string(pc->account, "access_token", NULL);
				
				if (photo_hash && access_token && (!avatar_hash || !g_str_equal(photo_hash, avatar_hash)))
				{
					struct BuddyHashPair *bhp = g_new0(struct BuddyHashPair, 1);
					//request the new icon
					gchar *url = g_strconcat("https://graph.facebook.com/", jid_to_uid(buddy->name), "/picture?type=large&access_token=", access_token, "&return_ssl_resources=1", NULL);
					
					bhp->buddy = buddy;
					bhp->hash = g_strdup(photo_hash);
					fb_util_get_url(url, fb_got_big_picture, bhp);
					
					g_free(url);
				}
				//mangle the old hash so that the jabber prpl doesn't pick up on it
				g_free(photo->name); photo->name = g_strdup("old_photo");
				
				g_free(photo_hash);
			}
		}
	}
	
}

static void fb_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *userinfo, gboolean full)
{
	PurplePresence *presence;
	PurpleStatus *status;
	const char *message;
	gchar *ret;
	
	presence = purple_buddy_get_presence(buddy);
	status = purple_presence_get_active_status(presence);
	if((message = purple_status_get_attr_string(status, "message"))) {
		ret = g_markup_escape_text(message, -1);
		if (ret && *ret)
		{
			purple_notify_user_info_add_pair_html(userinfo, _("Status"), ret);
			g_free(ret);
		}
	}
}

static gboolean handle_jabber_receiving_iq_signal(PurpleConnection *pc, const char *type, const char *id, const char *from, xmlnode *iq)
{
	FacebookJabberStream *fjs = pc->proto_data;
	xmlnode *query, *ownmessage;
	
	if (!g_str_equal(purple_plugin_get_id(pc->prpl), FACEBOOK_PLUGIN_ID))
		return FALSE;
	
	// Remove groups if we're not using them
	if ((query = xmlnode_get_child(iq, "query")) && g_str_equal(xmlnode_get_namespace(query), "jabber:iq:roster"))
	{
		xmlnode *item;
		for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item))
		{
			if (purple_account_get_bool(fjs->account, "facebook_use_groups", TRUE) == FALSE)
			{
				xmlnode *group;
				for(group = xmlnode_get_child(item, "group"); group; group = xmlnode_get_child(item, "group")) {
					xmlnode_free(group);
				}
				if (!purple_find_buddy(pc->account, xmlnode_get_attrib(item, "jid")))
				{
					group = xmlnode_new("group");
					xmlnode_insert_data(group, "Facebook", -1);
					xmlnode_insert_child(item, group);
				}
			}
			//Hack the JID to just be the Facebook ID number
			//xmlnode_set_attrib(item, "jid", jid_to_uid(xmlnode_get_attrib(item, "jid")));
		}
		if (purple_account_get_bool(fjs->account, "facebook_use_groups", TRUE) == FALSE)
		{
			purple_debug_info("facebook", "Just disabled groups/friend lists\n");
		}
	} else if ((ownmessage = xmlnode_get_child(iq, "own-message")))
	{
		// TODO make sure we haven't already received the message
		
		const gchar *to = xmlnode_get_attrib(ownmessage, "to");
		gchar *message = xmlnode_get_data(xmlnode_get_child(ownmessage, "body"));
		if (!g_hash_table_remove(fjs->sent_messages_hash, message))
		{
			gchar *message_text = fb_replace_styled_text(message);
			serv_got_im(pc, to, message_text, PURPLE_MESSAGE_SEND, time(NULL));
			g_free(message_text);
		}
		g_free(message);
		
		return TRUE;
	}
	
	return FALSE;
}

static void fb_jabber_login(PurpleAccount *account)
{
	FacebookJabberStream *fjs;
	JabberStream *js;
	PurpleConnection *pc = purple_account_get_connection(account);
	gchar *old_username;
	gchar *new_username;
	const gchar *alias;

	purple_account_set_bool(account, "require_tls", FALSE);
	purple_account_set_bool(account, "old_ssl", FALSE);
	purple_account_set_bool(account, "auth_plain_in_clear", TRUE);
	purple_account_set_int(account, "port", 5222);
	purple_account_set_string(account, "connect_server", "chat.facebook.com");
	purple_account_set_string(account, "ft_proxies", "");
	purple_account_set_bool(account, "custom_smileys", FALSE);
	
	account->password = g_strstrip(account->password);
	
	/* Hack the username so that it's a valid XMPP username */
	old_username = account->username;
	new_username = g_strconcat(purple_url_encode(old_username), "@", "chat.facebook.com", "/", "Pidgin", NULL);
	account->username = new_username;
	jabber_login(account);
	pc->flags &= ~(PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY);
	js = pc->proto_data;
	account->username = old_username;
	g_free(new_username);
	
	if (js == NULL)
	{
		return;
	}
	
	fjs = g_new0(FacebookJabberStream, 1);
	fjs->js = *js;
	fjs->pc = pc;
	fjs->account = account;
	pc->proto_data = fjs;
	
	fjs->last_notification_time = purple_account_get_int(account, "last_notification_time", 0);
	
	// Listen to roster events
	jabber_iq_signal_register("query", "jabber:iq:roster");
	jabber_iq_signal_register("own-message", "http://www.facebook.com/xmpp/messages");
	
	/* Have we authorised access to Facebook on this account? */
	if (purple_account_get_string(account, "access_token", NULL) == NULL)
	{
		// Authorise the account
		fb_request_auth();
		fb_request_access_token(pc);
	} else {
		alias = purple_account_get_alias(account);
		if (!alias || !*alias)
		{
			fql_query(pc, "SELECT uid,name FROM user WHERE uid=me()", fb_got_account_alias, NULL, NULL);
		}
		
		fjs->status_message_timeout = purple_timeout_add_seconds(60, (GSourceFunc) fb_fetch_status_messages, pc);
		
		fjs->unread_count_timeout = purple_timeout_add_seconds(60, (GSourceFunc) fb_fetch_unread_count, pc);
		
		fjs->notifications_timeout = purple_timeout_add_seconds(60, (GSourceFunc) fb_fetch_notifications, pc);
	}
	
	fjs->sent_messages_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
}

static void fb_jabber_close(PurpleConnection *pc)
{
	FacebookJabberStream *fjs = pc->proto_data;
	if (fjs->status_message_timeout) purple_timeout_remove(fjs->status_message_timeout);
	if (fjs->unread_count_timeout) purple_timeout_remove(fjs->unread_count_timeout);
	if (fjs->notifications_timeout) purple_timeout_remove(fjs->notifications_timeout);
	
	purple_account_set_int(pc->account, "last_notification_time", fjs->last_notification_time);
	
	g_hash_table_destroy(fjs->sent_messages_hash);
	
	// Free our variables first as jabber_close() will free the rest
	g_free(fjs->last_status_message);
	jabber_iq_signal_unregister("query", "jabber:iq:roster");
	jabber_iq_signal_unregister("own-message", "http://www.facebook.com/xmpp/messages");
	
	jabber_close(pc);
}

static GList *
fb_status_types(PurpleAccount *account)
{
	PurpleStatusType *type;
	PurpleValue *priority_value;
	GList *types = NULL;
	
	priority_value = purple_value_new(PURPLE_TYPE_INT);
	purple_value_set_int(priority_value, 1);
	
	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
			jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_ONLINE),
			NULL, TRUE, TRUE, FALSE,
			"priority", _("Priority"), priority_value,
			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
			NULL);
	types = g_list_append(types, type);
	
	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
			jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_AWAY),
			NULL, TRUE, TRUE, FALSE,
			"priority", _("Priority"), priority_value,
			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
			NULL);
	types = g_list_append(types, type);
	
	type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE,
			jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE),
			NULL, TRUE, TRUE, FALSE,
			"priority", _("Priority"), priority_value,
			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
			NULL);
	types = g_list_append(types, type);
	
	return types;
}

static GHashTable *fb_get_account_text_table(PurpleAccount *account)
{
	GHashTable *table;
	
	table = g_hash_table_new(g_str_hash, g_str_equal);
	g_hash_table_insert(table, "login_label", (gpointer)_("Facebook Username..."));
	
	return table;
}

static void
fb_signed_on_cb(PurpleConnection *pc, void *data)
{
	purple_debug_info("facebook", "signed-on\n");
	pc->state = PURPLE_CONNECTED;
	
	if (purple_account_get_string(pc->account, "access_token", NULL) != NULL)
	{
		fb_fetch_status_messages(pc);
		fb_fetch_unread_count(pc);
		fb_fetch_notifications(pc);
	}
}

static gboolean load_fb_plugin(PurplePlugin *plugin)
{
	purple_debug_info("facebook", "plugin_load %s\n", purple_plugin_get_id(plugin));

	purple_signal_register(plugin, "jabber-receiving-xmlnode",
			purple_marshal_VOID__POINTER_POINTER, NULL, 2,
			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
			purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));

	purple_signal_register(plugin, "jabber-sending-xmlnode",
			purple_marshal_VOID__POINTER_POINTER, NULL, 2,
			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
			purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));

	/*
	 * Do not remove this or the plugin will fail. Completely. You have been
	 * warned!
	 */
	purple_signal_connect_priority(plugin, "jabber-sending-xmlnode",
			plugin, PURPLE_CALLBACK(jabber_send_signal_cb),
			NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST);

	purple_signal_register(plugin, "jabber-sending-text",
			     purple_marshal_VOID__POINTER_POINTER, NULL, 2,
			     purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
			     purple_value_new_outgoing(PURPLE_TYPE_STRING));

	purple_signal_register(plugin, "jabber-receiving-message",
			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER,
			purple_value_new(PURPLE_TYPE_BOOLEAN), 6,
			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
			purple_value_new(PURPLE_TYPE_STRING), /* type */
			purple_value_new(PURPLE_TYPE_STRING), /* id */
			purple_value_new(PURPLE_TYPE_STRING), /* from */
			purple_value_new(PURPLE_TYPE_STRING), /* to */
			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));

	purple_signal_register(plugin, "jabber-receiving-iq",
			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
			purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
			purple_value_new(PURPLE_TYPE_STRING), /* type */
			purple_value_new(PURPLE_TYPE_STRING), /* id */
			purple_value_new(PURPLE_TYPE_STRING), /* from */
			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));

	purple_signal_register(plugin, "jabber-watched-iq",
			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
			purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
			purple_value_new(PURPLE_TYPE_STRING), /* type */
			purple_value_new(PURPLE_TYPE_STRING), /* id */
			purple_value_new(PURPLE_TYPE_STRING), /* from */
			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); /* child */

	/* Modifying these? Look at jabber_init_plugin for the ipc versions */
	purple_signal_register(plugin, "jabber-register-namespace-watcher",
			purple_marshal_VOID__POINTER_POINTER,
			NULL, 2,
			purple_value_new(PURPLE_TYPE_STRING),  /* node */
			purple_value_new(PURPLE_TYPE_STRING)); /* namespace */

	purple_signal_register(plugin, "jabber-unregister-namespace-watcher",
			purple_marshal_VOID__POINTER_POINTER,
			NULL, 2,
			purple_value_new(PURPLE_TYPE_STRING),  /* node */
			purple_value_new(PURPLE_TYPE_STRING)); /* namespace */

	purple_signal_connect(plugin, "jabber-register-namespace-watcher",
			plugin, PURPLE_CALLBACK(jabber_iq_signal_register), NULL);
	purple_signal_connect(plugin, "jabber-unregister-namespace-watcher",
			plugin, PURPLE_CALLBACK(jabber_iq_signal_unregister), NULL);

	purple_signal_register(plugin, "jabber-receiving-presence",
			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER,
			purple_value_new(PURPLE_TYPE_BOOLEAN), 4,
			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
			purple_value_new(PURPLE_TYPE_STRING), /* type */
			purple_value_new(PURPLE_TYPE_STRING), /* from */
			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));

	
	//we want to hook into the "jabber-receiving-xmlnode" signal to be able to listen to our connect event
	purple_signal_connect(plugin, "jabber-receiving-xmlnode", plugin,
						  PURPLE_CALLBACK(handle_jabber_receiving_xmlnode_signal), NULL);

	//intercept iq in case it has roster info with groups
	purple_signal_connect(plugin, "jabber-receiving-iq", plugin,
						  PURPLE_CALLBACK(handle_jabber_receiving_iq_signal), NULL);
	
	
	purple_signal_connect(purple_connections_get_handle(), "signed-on", plugin, 
						  PURPLE_CALLBACK(fb_signed_on_cb), NULL);
	
	jabber_auth_add_mech(&fb_sasl_mech);
	
	return TRUE;
}

static gboolean unload_fb_plugin(PurplePlugin *plugin)
{
	purple_signals_unregister_by_instance(plugin);
	purple_signals_disconnect_by_handle(plugin);
	
	jabber_auth_remove_mech(&fb_sasl_mech);
	
	return TRUE;
}

static void fb_connect(PurplePluginAction *action)
{
	fb_request_auth();
	
	// Display a popup to enter the access_token
}

static GList *fb_actions(PurplePlugin *plugin, gpointer context)
{
	GList *m = NULL;
	PurplePluginAction *act;
	PurpleConnection *pc = context;
	PurpleAccount *account = pc->account;
	
	if (purple_account_get_string(account, "access_token", NULL) == NULL)
	{
		act = purple_plugin_action_new(_("Connect to Facebook"), fb_connect);
		m = g_list_append(m, act);
		
		act = purple_plugin_action_new(_("Set Access Token"), fb_set_access_token);
		m = g_list_append(m, act);
	} else
	{
		act = purple_plugin_action_new(_("Set Facebook status..."), fb_set_status_cb);
		m = g_list_append(m, act);

		//act = purple_plugin_action_new(_("Search for buddies..."), fb_search_users);
		//m = g_list_append(m, act);
	}
	return m;
}

static gint fb_message_send_im(PurpleConnection *pc, const gchar *who, const gchar *msg,
		PurpleMessageFlags flags)
{
	gint retval;
	FacebookJabberStream *fjs = pc->proto_data;
	
	g_hash_table_insert(fjs->sent_messages_hash, g_strdup(msg), NULL);
	retval = jabber_message_send_im(pc, who, msg, flags);
	
	return retval;
}

static void fb_buddy_got_info(PurpleConnection *pc, const xmlnode *xmldata, gpointer user_data)
{
	PurpleNotifyUserInfo *user_info;
	gchar *who = user_data;
	gchar *value, *data;
	xmlnode *user;
	
	user = xmlnode_get_child(xmldata, "user");
	user_info = purple_notify_user_info_new();
	
	data = xmlnode_get_data(xmlnode_get_child(user, "profile_url"));
	value = g_strdup_printf("<a href=\"%s\">%s</a>", data, _("View web profile"));
	purple_notify_user_info_add_pair_html(user_info, NULL, value);
	g_free(data);
	g_free(value);
	
	purple_notify_user_info_add_section_break(user_info);
	
	if (xmlnode_get_child(xmlnode_get_child(user, "status"), "message"))
	{
		value = xmlnode_get_data_unescaped(xmlnode_get_child(xmlnode_get_child(user, "status"), "message"));
		purple_notify_user_info_add_pair_html(user_info, _("Status"), value);
		g_free(value);
	}
	
	//about you
	value = xmlnode_get_data_unescaped(xmlnode_get_child(user, "about_me"));
	purple_notify_user_info_add_pair_html(user_info, _("About You"), value);
	g_free(value);
	
	//relationship status
	value = xmlnode_get_data_unescaped(xmlnode_get_child(user, "relationship_status"));
	purple_notify_user_info_add_pair_html(user_info, _("Relationship Status"), value);
	g_free(value);
	
	//gender - sex is not gender :s
	value = xmlnode_get_data_unescaped(xmlnode_get_child(user, "sex"));
	purple_notify_user_info_add_pair_html(user_info, _("Gender"), value);
	g_free(value);

	//website
	value = xmlnode_get_data_unescaped(xmlnode_get_child(user, "website"));
	purple_notify_user_info_add_pair_html(user_info, _("Website"), value);
	g_free(value);
	
	//email
	value = xmlnode_get_data_unescaped(xmlnode_get_child(user, "email"));
	purple_notify_user_info_add_pair_html(user_info, _("Email"), value);
	g_free(value);
		
	purple_notify_userinfo(pc, who, user_info, NULL, NULL);
	purple_notify_user_info_destroy(user_info);
	
	g_free(who);
}

static void fb_buddy_get_info(PurpleConnection *pc, const char *who)
{
	gchar *query = g_strdup_printf("SELECT uid,name,affiliations,religion,birthday_date,sex,hometown_location,meeting_sex,meeting_for,relationship_status,significant_other_id,political,current_location,activities,interests,is_app_user,music,tv,movies,books,quotes,about_me,hs_info,education_history,work_history,status,online_presence,locale,profile_url,profile_blurb,username,website,is_blocked,email FROM user WHERE uid=%s", jid_to_uid(who));
	
	fql_query(pc, query, fb_buddy_got_info, NULL, g_strdup(who));

	g_free(query);
}

static const char *fb_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
{
	return "facebook";
}

static PurplePluginProtocolInfo prpl_info =
{
#if PURPLE_MAJOR_VERSION > 2
	sizeof(prpl_info),
#endif
	OPT_PROTO_MAIL_CHECK |
	OPT_PROTO_PASSWORD_OPTIONAL |
	//OPT_PROTO_NO_PASSWORD |
	OPT_PROTO_SLASH_COMMANDS_NATIVE,
	NULL,							/* user_splits */
	NULL,							/* protocol_options */
	{"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
	fb_list_icon,					/* list_icon */
	NULL,							/* list_emblems */
	jabber_status_text,				/* status_text */
	fb_tooltip_text,				/* tooltip_text */
	fb_status_types,				/* status_types */
	jabber_blist_node_menu,			/* blist_node_menu */
	NULL,							/* chat_info */
	NULL,							/* chat_info_defaults */
	fb_jabber_login,				/* login */
	fb_jabber_close,				/* close */
	fb_message_send_im,				/* send_im */
	NULL,							/* set_info */
	jabber_send_typing,				/* send_typing */
	fb_buddy_get_info,				/* get_info */
	fb_set_status_p,				/* set_status */
	jabber_idle_set,				/* set_idle */
	NULL,							/* change_passwd */
	NULL/*fb_add_buddy*/,				/* add_buddy */
	NULL,							/* add_buddies */
	NULL/*fb_remove_buddy*/,			/* remove_buddy */
	NULL,							/* remove_buddies */
	NULL,							/* add_permit */
	NULL,							/* add_deny */
	NULL,							/* rem_permit */
	NULL,							/* rem_deny */
	NULL,							/* set_permit_deny */
	NULL,							/* join_chat */
	NULL,							/* reject_chat */
	NULL,							/* get_chat_name */
	NULL,							/* chat_invite */
	NULL,							/* chat_leave */
	NULL,							/* chat_whisper */
	NULL,							/* chat_send */
	jabber_keepalive,				/* keepalive */
	NULL,							/* register_user */
	NULL,							/* get_cb_info */
#if PURPLE_MAJOR_VERSION < 3
	NULL,							/* get_cb_away */
#endif
	NULL,							/* alias_buddy */
	NULL/*fb_group_buddy_move*/,		/* group_buddy */
	NULL/*fb_group_rename*/,		/* rename_group */
	NULL/*fb_buddy_free*/,				/* buddy_free */
	jabber_convo_closed,			/* convo_closed */
	jabber_normalize,				/* normalize */
	NULL,							/* set_buddy_icon */
	NULL/*fb_group_remove*/,			/* remove_group */
	NULL,							/* get_cb_real_name */
	NULL,							/* set_chat_topic */
	NULL,							/* find_blist_chat */
	NULL,							/* roomlist_get_list */
	NULL,							/* roomlist_cancel */
	NULL,							/* roomlist_expand_category */
	NULL,							/* can_receive_file */
	NULL,							/* send_file */
	NULL,							/* new_xfer */
	NULL,							/* offline_message */
	NULL,							/* whiteboard_prpl_ops */
	jabber_prpl_send_raw,			/* send_raw */
	NULL, 							/* roomlist_room_serialize */
	NULL,							/* unregister_user */
	NULL,							/* send_attention */
	NULL,							/* attention_types */
#if PURPLE_MAJOR_VERSION < 2 || PURPLE_MAJOR_VERSION == 2 && PURPLE_MINOR_VERSION < 5
	(gpointer) sizeof(PurplePluginProtocolInfo)
#else
#if PURPLE_MAJOR_VERSION < 3
	sizeof(PurplePluginProtocolInfo),       /* struct_size */
#endif
	fb_get_account_text_table, /* get_account_text_table */
	NULL,							/* initiate_media */
	NULL,							/* get_media_caps */
#if PURPLE_MAJOR_VERSION >= 3 || PURPLE_MAJOR_VERSION == 2 && PURPLE_MINOR_VERSION >= 7
	NULL,                           /* get_moods */
	NULL,                           /* set_public_alias */
	NULL                            /* get_public_alias */
#endif
#endif
};
static void plugin_fb_init(PurplePlugin *plugin)
{
	PurpleAccountOption *option;
	PurplePluginInfo *info = plugin->info;
	PurplePluginProtocolInfo *prpl_info = info->extra_info;
	
	/* Add options to the advanced screen in the account settings */
	option = purple_account_option_bool_new(_("Show history in new conversations"), "facebook_show_history", TRUE);
	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
	
	option = purple_account_option_bool_new(_("Use Facebook friend-lists as Pidgin groups"), "facebook_use_groups", TRUE);
	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
	
	option = purple_account_option_bool_new(_("Hide myself in the Buddy List"), "facebook_hide_self", TRUE);
	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
	
	option = purple_account_option_bool_new(_("Set Facebook status through Pidgin status"), "facebook_set_status_through_pidgin", FALSE);
	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
	
	option = purple_account_option_bool_new(_("Show Facebook notifications as e-mails in Pidgin"), "facebook_get_notifications", TRUE);
	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
	
	option = purple_account_option_bool_new(_("Edit Facebook friends from Pidgin"), "facebook_manage_friends", FALSE);
	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
	
	option = purple_account_option_string_new(_("Access Token"), "access_token", "");
	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
	
	// We rely on the Jabber/XMPP protocol plugin to work
	info->dependencies = g_list_prepend(info->dependencies, "prpl-jabber");
}

static PurplePluginInfo info =
{
	PURPLE_PLUGIN_MAGIC,
	PURPLE_MAJOR_VERSION,
	PURPLE_MINOR_VERSION,
	PURPLE_PLUGIN_PROTOCOL,                             /**< type           */
	NULL,                                             /**< ui_requirement */
	0,                                                /**< flags          */
	NULL,                                             /**< dependencies   */
	PURPLE_PRIORITY_DEFAULT,                            /**< priority       */

	FACEBOOK_PLUGIN_ID,                                    /**< id             */
	"Facebook (New)",                                           /**< name           */
	"2.0",                                  /**< version        */
	                                                  /**  summary        */
	N_("Facebook Protocol Plugin"),
	                                                  /**  description    */
	N_("Facebook Protocol Plugin"),
	"Eion Robb <eionrobb@gmail.com>",                  /**< author         */
	"http://pidgin-facebookchat.googlecode.com/",                                     /**< homepage       */

	load_fb_plugin,                                      /**< load           */
	unload_fb_plugin,                                    /**< unload         */
	NULL,                                             /**< destroy        */

	NULL,                                             /**< ui_info        */
	&prpl_info,                                       /**< extra_info     */
	NULL,                                             /**< prefs_info     */
	fb_actions,

	/* padding */
	NULL,
	NULL,
	NULL,
	NULL
};

PURPLE_INIT_PLUGIN(facebook, plugin_fb_init, info);
